L.History = L.Class.extend({

  initialize: function(roomId, execute, rollback, couplingOptions){
    this._roomId = roomId;
    this._undoStack = [];
    this._redoStack = [];
    this._waitingQueue = [];
    this._execute = this._isFn(execute, "initialize") ? execute : function() {};
    this._rollback = this._isFn(rollback,"initialize") ? rollback : function() {};
    
    if ( couplingOptions != null && couplingOptions.enabled )
    	this._couple(couplingOptions);
  },

  addCommand: function(command){
    // verificar que no es un tipo usado internamente por History
    if(command.type == '_undo_' || command.type == '_redo_'){
      console.log('[WARNING] L.History.addCommand no se puede utilizar command.type _undo_ o _redo_');
    }

    command['id']= new ObjectId().toString();
    command['roomId'] = this._roomId;

    //se ejecuta inmediatamente el comando y se pone en la cola de espera por confirmacion.
    if(command.type != "ping"){
      this._executeCommand(command);
      this._waitingQueue.push(command);
    }

    //enviar el comando al servidor
    //this._sendCommandToServer(command);    
  },

  undo: function() {
    if (this.undoable()) {
      // enviar comando al servidor.
      var command = {
        id: new ObjectId().toString(),
        type: '_undo_',
        roomId: this._roomId
      };
      this._executeCommand(command);
      //this._sendCommandToServer(command);
    }
  },

  redo: function() {
    if (this.redoable()) {
      // enviar comando al servidor
      var command = {
        id: new ObjectId().toString(),
        type: '_redo_',
        roomId: this._roomId
      };
      this._executeCommand(command);
      //this._sendCommandToServer(command);
    }    
  },

  undoable: function(){
    return this._undoStack.length > 0;
  },

  redoable: function(){
    return this._redoStack.length > 0;
  },

//  receiveCommandFromServer: function(command){
//    if(this._waitingQueue.length && command.id == this._waitingQueue[0].id){
//      //el comando ya fue ejecutado y solo esperaba confirmacion.
//      this._waitingQueue.shift();
//      return;
//    }
//
//    //deshacer comandos que fueron optimistamente ejecutados antes de su confirmacion.
//    for(var n=this._waitingQueue.length-1; n>=0; n--){
//      this._rollback(this._waitingQueue[n]);
//    }
//    this._waitingQueue = [];
//    this._executeCommand(command);
//  },

  _executeCommand: function(command){ 
    var us = this._undoStack;
    var rs = this._redoStack;

    switch(command.type){
      case '_undo_':
        var lastCommand = us.pop();
        rs.push(lastCommand);
        this._rollback(lastCommand);
        break;
      case '_redo_':
        var lastCommand = rs.pop();
        us.push(lastCommand);
        this._execute(lastCommand);
        break;
      default:
        us.push(command);
        this._execute(command);
    }
  },

  _isFn: function(fn, FName, echoOff){
    var is = typeof fn == 'function';
    if (!is && !echoOff)
      console.log('[WARNING] L.History.' + FName + ' fn no es una funcion');
    return is;
  },
  
  _couple:function( couplingOptions ) {
	  couplingOptions.adapter.coupleObject( 
		  couplingOptions.carriedId + "-" + this._roomId + "-history", 
		  this, 
		  {
			  explicitMapping:[ "addCommand", "undo", "redo"],
			  messageType:"EVENT"
		  }
	  );
  },

//  useAdapter: function(){
//    var self = this;
//    var historyId =  this._historyId;
//	var adapters = L.clientAdapters;
//	//console.log(adapters);
//	var adapter = adapters[historyId] = new ClientAdapter(config.SERVER_URL);
//	adapter.joinSession(historyId);
//	adapter.coupleObject(historyId, self, {
//		messageType : "EVENT",
//		explicitMapping : [ "addCommand", "undo", "redo"]
//	});
//	//adapter.decouple(roomId);
//	adapter.start();
//	adapter.couple(historyId);
////    var handler = new Handler(clientAdapter,{
////      objectId: roomId,
////      type: 'EVENT',
////      echo: true,
////      receiveEvent: function(object, content){
////        self.receiveCommandFromServer(content)
////      }
////    });
////
////    this._sendCommandToServer  = function(content){
////      handler.sendEvent(roomId, content); 
////    }
////    clientAdapter.couple(roomId, handler);
//    
//  }  

});







